const style = {color: 'green', border: '1px solid red', margin: '5px'};

/*const virtualDOM_A = (
  <div key='A' style={style}>
    <div key='B1' style={style}></div>
    <div key='B2' style={style}></div>
  </div>
);*/
const virtualDOM_A = {
  type: 'div',
  key: 'A',
  props: {
    style,
    children: [
      {type: 'div', key: 'B1', props: {style, children: []}},
      {type: 'div', key: 'B2', props: {style, children: []}}
    ]
  }
}

//--------------------------------------------------------------------
const TAG_ROOT = 'TAG_ROOT';
const TAG_HOST = 'TAG_HOST'; //原生DOM节点 div span p ...
const PLACEMENT = 'PLACEMENT';
const root = document.getElementById('root');

let workInProgress;

const rootFiber = {
  tag: TAG_ROOT, //Fiber类型
  key: 'ROOT', //唯一标识
  stateNode: root, //Fiber对应的真实DOM节点
  props: {children: [virtualDOM_A]}
}

workInProgress = rootFiber;
workLoop();


//--------------------------------------------------------------------
function workLoop() {
  while (workInProgress) {
    workInProgress = performUnitOfWork(workInProgress)/*←执行完后会返回下一个任务*/;
  }

  console.log('rootFiber:',rootFiber);
  commitRoot(rootFiber);
}

function commitRoot(rootFiber){
  let currentEffect = rootFiber.firstEffect;

  while (currentEffect) {
    const flags = currentEffect.flags;

    switch (flags) {
      case PLACEMENT:
        commitPlacement(currentEffect);
        break
    }

    currentEffect = currentEffect.nextEffect;
  }
}

function commitPlacement(currentEffect) {
  const parent = currentEffect.return.stateNode;
  parent&&parent.appendChild(currentEffect.stateNode);
}

/**
 *
 * @param fiber 当前正在处理的fiber
 *
 * 1. 根据传入的fiber 构建 fiber树 (添加child、sibling、return等属性)
 * 2. 创建真实DOM
 *
 * 注意：
 * 一个performUnitOfWork工作期间内是无法暂停的, 即它是react 最小的工作单位了
 */
function performUnitOfWork(fiber) {
  console.log('performUnitOfWork---');
  console.log(`准备为 key为: '${fiber.key}' 的fiber 构建fiber树...`);
  beginWork(fiber);
  console.log(`key为: '${fiber.key}' 的fiber fiber树构建完毕`);

  //有了↑面构建的fiber树 ↓面才好按顺序遍历

  //1. 先找儿子
  if (fiber.child) {
    return fiber.child; //← 如果有儿子会继续往下构建 fiber树, 直到没有儿子, 儿子也没有其它弟弟了, 此时就会找叔叔, 继续构建fiber树
  }

  while (fiber){
    completeUnitOfWork(fiber); //能执行到这里 说明当前fiber没有儿子 直接完成该节点

    //2. 没有儿子 找弟弟
    if (fiber.sibling) {
      return fiber.sibling; //← 儿子还有弟弟, 此时fiber树还没构建完, 将弟弟返回继续进行fiber树的构建(重头执行 performUnitOfWork)
    }

    //执行到这里并不能说明整个fiber树构建完了, 因为可能他还有叔叔, 叔叔还有儿子什么的 ...

    //3. 没有弟弟 找叔叔(这里会返回爸爸 进入下一次while, 下一次while会直接fiber.sibling 也就是叔叔 ... 以此这般 直到根
    fiber = fiber.return;

    //如果.return 没有返回值了, 就说明fiber树构建完毕了
    //在fiber树构建完毕的过程中 儿子先遍历完的节点 会先 completeUnitOfWork
  }
}


/**
 * 根据当前父Fiber 和 其儿子们的虚拟DOM 构建 fiber树
 * @param beginWork
 */
function beginWork(beginWork) {
  console.log('beginWork:', workInProgress.key);

  const nextChildren = workInProgress.props.children;

  return reconcileChildren(workInProgress/*父fiber*/, nextChildren/*儿子们的虚拟dom*/);
}

//↓ 构建出来的具体情况 查看 99mini-react-fiber/assets/fiber2.png
function reconcileChildren(returnFiber/*就是当前孩子们的爹*/, nextChildren) {
  let previousNewFiber, //上一个儿子
    firstChildFiber; //当前returnFiber的大儿子

  for (let i = 0; i < nextChildren.length; i++) {
    const newFiber = createFiber(nextChildren[i]);
    newFiber.flags = PLACEMENT

    newFiber.return = returnFiber;

    !firstChildFiber ?
      firstChildFiber = newFiber :
      previousNewFiber.sibling = newFiber;

    previousNewFiber = newFiber;
  }

  returnFiber.child = firstChildFiber;
  return firstChildFiber;
}

function createFiber(vdom) {
  return {
    tag: TAG_HOST,
    type: vdom.type, //具体的类型 div、p、span、 组件、 REACT_TEXT(文字和数字)
    key: vdom.key,
    props: vdom.props
  }
}

/**
 * 一个Fiber节点在结束的时候要去创建真实的DOM元素
 * @param fiber
 */
function completeUnitOfWork(fiber){
  console.log('completeUnitOfWork:', fiber.key);
  let stateNode; //真实DOM

  switch (fiber.tag) {
  case TAG_HOST:
    stateNode = createStateNode(fiber);
    break;
  }

  // 在完成工作单元的时候 要判断 当前的fiber节点 有没有对应的DOM操作
  makeEffectList(fiber)
}

/**
 * 创建真实DOM节点
 * @param fiber
 */
function createStateNode(fiber){
  if (fiber.tag === TAG_HOST) {
    const stateNode = document.createElement(fiber.type);
    fiber.stateNode = stateNode;
  }

  return fiber.stateNode;
}

/**
 * 创建 副作用链 (不是包含所有fiber节点,而是包括有副作用的fiber节点,对于初次渲染而言 都要包含)
 * 是一个单链表
 *
 * 归并思想
 *
 * @param fiber
 *
 * makeEffectList 是在 completeUnitOfWork 里调用的
 * completeUnitOfWork 是 遍历时, 谁的儿子先遍历完, 谁就先completeUnitOfWork
 * 也就是说effectList 是从下面往上面 搜集的
 *
       A
   B1     B2
 C1  C2 D1  D2

 收集到的effect链是这样的 → C1 C2 B1 D1 D2 B2
 和完成链的顺序是一样的(只是没有副作用的不会挂上去)
*/
function makeEffectList(fiber) {
  const returnFiber = fiber.return;

  if(returnFiber){
    //1. 先把自己儿子的effect挂到父亲身上
    if (!returnFiber.firstEffect) { //如果爹没有effectList, 那么儿子的就是爹的
      returnFiber.firstEffect = fiber.firstEffect;
    }
    if (fiber.lastEffect) {
      returnFiber.lastEffect && (returnFiber.lastEffect.nextEffect = fiber.firstEffect);
      returnFiber.lastEffect = fiber.lastEffect;
    }

    //2. 再把自己挂到父亲身上
    //第一次其实会走这里↓↓↓ (而不是上面↑
    //如果fiber节点有flags 才会被搜集
    if(fiber.flags){
      if (returnFiber.lastEffect) {
        returnFiber.lastEffect.nextEffect = fiber;
      }else {
        returnFiber.firstEffect = fiber; //← 第一次走这里
      }
      returnFiber.lastEffect = fiber;
    }
  }
}
